package by.pavlyukevich.paymentsystem.dataaccess.impl;

import java.util.ArrayList;
import java.util.List;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.JoinType;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;

import org.springframework.stereotype.Repository;

import by.pavlyukevich.paymentsystem.dataaccess.TransactionDao;
import by.pavlyukevich.paymentsystem.dataaccess.filter.Filter;
import by.pavlyukevich.paymentsystem.dataaccess.filter.TransactionFilter;
import by.pavlyukevich.paymentsystem.datamodel.Transaction;
import by.pavlyukevich.paymentsystem.datamodel.Transaction_;

@Repository
public class TransactionDaoImpl extends AbstractDaoImpl<Integer, Transaction> implements
		TransactionDao {

	protected TransactionDaoImpl() {
		super(Transaction.class);
	}

	@Override
	public List<Transaction> getList(Filter<Transaction> transactionFilter) {

		CriteriaQuery<Transaction> criteria = getAllCriteria(transactionFilter);

		addCriteriaRestrict(criteria, transactionFilter);

		Root<Transaction> root = getRoot(criteria, Transaction.class);
		root.fetch(Transaction_.payeeAccount);
		root.fetch(Transaction_.senderAccount);
		root.fetch(Transaction_.senderCard, JoinType.LEFT);

		return getList(criteria, transactionFilter);
	}

	@Override
	public long getCount(Filter<Transaction> transactionFilter) {
		CriteriaQuery<Long> criteria = getCountAllCriteria();
		addCriteriaRestrict(criteria, transactionFilter);
		return getSingle(criteria);
	}

	private <T> CriteriaQuery<T> addCriteriaRestrict(CriteriaQuery<T> criteria,
			Filter<Transaction> filter) {
		if (filter == null) {
			return criteria;
		}
		Root<Transaction> root = getRoot(criteria, Transaction.class);
		CriteriaBuilder cBuilder = getEntityManager().getCriteriaBuilder();
		List<Predicate> predicateList = new ArrayList<Predicate>();
		TransactionFilter transactionFilter = (TransactionFilter) filter;

		if (transactionFilter.getSenderAccount() != null) {
			predicateList.add(cBuilder.equal(root.get(Transaction_.senderAccount),
					transactionFilter.getSenderAccount()));
		}
		if (transactionFilter.getSenderCard() != null) {
			predicateList.add(cBuilder.equal(root.get(Transaction_.senderCard),
					transactionFilter.getSenderCard()));
		}
		if (transactionFilter.getPayeeAccount() != null) {
			predicateList.add(cBuilder.equal(root.get(Transaction_.payeeAccount),
					transactionFilter.getPayeeAccount()));
		}
		if (transactionFilter.getBeginPeriod() != null) {
			predicateList.add(cBuilder.greaterThanOrEqualTo(root.get(Transaction_.date),
					transactionFilter.getBeginPeriod()));
		}
		if (transactionFilter.getEndPeriod() != null) {
			predicateList.add(cBuilder.lessThanOrEqualTo(root.get(Transaction_.date),
					transactionFilter.getEndPeriod()));
		}

		if (predicateList.size() > 0) {
			Predicate predicateArray[] = predicateList.toArray(new Predicate[0]);
			criteria.where(predicateArray);
		}

		return criteria;
	}

	@Override
	public Double getSum(TransactionFilter transactionFilter) {
		CriteriaBuilder cBuilder = getEntityManager().getCriteriaBuilder();
		CriteriaQuery<Double> criteria = cBuilder.createQuery(Double.class);
		Root<Transaction> root = criteria.from(Transaction.class);
		criteria.select(cBuilder.sum(root.get(Transaction_.amount)));
		addCriteriaRestrict(criteria, transactionFilter);
		return getSingle(criteria);
	}
}
